package com.yunbiao.ybbusstopplate.refactoring.ui.dalian;

import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;

import com.blankj.utilcode.util.CollectionUtils;
import com.blankj.utilcode.util.FileIOUtils;
import com.blankj.utilcode.util.GsonUtils;
import com.blankj.utilcode.util.ScreenUtils;
import com.blankj.utilcode.util.ThreadUtils;
import com.blankj.utilcode.util.TimeUtils;
import com.blankj.utilcode.util.UiMessageUtils;
import com.example.library_hy_advert.system.Cache;
import com.example.library_hy_advert.system.Path;
import com.google.gson.reflect.TypeToken;
import com.yunbiao.ybbusstopplate.BuildConfig;
import com.yunbiao.ybbusstopplate.refactoring.Config;
import com.yunbiao.ybbusstopplate.refactoring.agreement.base.ProtocolParser;
import com.yunbiao.ybbusstopplate.refactoring.ui.yibin.ZHXPortAgreement;
import com.yunbiao.ybbusstopplate.refactoring.utils.L;
import com.yunbiao.ybbusstopplate.refactoring.utils.Utils;

import java.io.File;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;

import tp.xmaihh.serialport.SerialHelper;
import tp.xmaihh.serialport.bean.ComBean;

public class ZHIDAParser extends ProtocolParser {
    private static final String TAG = "ZHIDAParser";

    //上行站点
    private static final String KEY_UP_LIST = "key_zhida_up_list";
    //下行站点
    private static final String KEY_DOWN_LIST = "key_zhida_down_list";


    //最后一次报站的方向
    private static final String KEY_LAST_DIRECTION = "key_zhida_last_direction";
    //最后一次报站的站号
    private static final String KEY_LAST_INDEX = "key_zhida_last_index";
    //最后一次报站的进出站
    private static final String KEY_LAST_INOUT = "key_zhida_last_inout";
    //最后一次报站的站点类型
    private static final String KEY_LAST_SITE_TYPE = "key_zhida_last_site_type";


    //线路名
    private static final String KEY_LINE_NAME = "key_zhida_line_name";
    //上行首末站
    private static final String KEY_UP_START_END_SITE = "key_zhida_up_start_end_site";
    //下行首末站
    private static final String KEY_DOWN_START_END_SITE = "key_zhida_down_start_end_site";
    //上行首末时间
    private static final String KEY_UP_START_END_TIME = "key_zhida_up_start_end_time";
    //下行首末时间
    private static final String KEY_DOWN_START_END_TIME = "key_zhida_down_start_end_time";
    //票价
    private static final String KEY_PRICE = "key_zhida_price";
    //上行首末时间
    private static final String KEY_UP_SPECIAL_START_END_TIME = "key_zhida_up_special_start_end_time";
    //下行首末时间
    private static final String KEY_DOWN_SPECIAL_START_END_TIME = "key_zhida_down_special_start_end_time";

    //固定显示内容
    public static final String KEY_FIXED_PROMOTION_SLOGAN = "key_zhida_fixed_promotion_slogan";

    //设备SN码
    private static final String KEY_SN_CODE = "key_zhida_sn_code";
    //播放宣传用语
    private static final String KEY_PROMOTION_SLOGAN = "key_zhida_promotion_slogan";
    private static final String DEFAULT_PROMOTION_SLOGAN = "欢迎乘坐本线路公交车，请文明乘车。请保持车厢环境整洁，严禁吸烟，请勿乱扔废弃物。严禁携带易燃易爆危险品上车。";
//    private static final String DEFAULT_PROMOTION_SLOGAN = "这是一串滚动文字，用于测试滚动字母功能";


    private final LinkedHashMap<Integer,Site> upMap = new LinkedHashMap<>();
    private final LinkedHashMap<Integer,Site> downMap = new LinkedHashMap<>();

    private final ExecutorService executorService;

    public ZHIDAParser() {
        executorService = ThreadUtils.getSinglePool();
    }

    //帧格式
    //81 帧起始符
    //00 目的地址
    //00 源地址
    //00 控制码
    //00 主标识
    //00 包序号
    //0000 数据长度域
    //00 副1标识
    //0000 副1长度
    //.... 副1内容
    //00 校验码
    //58 帧结束符

    @Override
    public void loadCache() {
        executorService.submit(() -> {
            try{
                String string = Cache.getString(KEY_UP_LIST);
                if(!TextUtils.isEmpty(string)){
                    List<Site> list = GsonUtils.fromJson(string,new TypeToken<List<Site>>(){}.getType());
                    upMap.clear();
                    for (Site site : list) {
                        upMap.put(site.getIndex(),site);
                    }
                }
            } catch (Exception e){
                L.e(TAG, "KEY_UP_LIST: ", e);
            }

            try {
                String string = Cache.getString(KEY_DOWN_LIST);
                if(!TextUtils.isEmpty(string)){
                    List<Site> list = GsonUtils.fromJson(string,new TypeToken<List<Site>>(){}.getType());
                    downMap.clear();
                    for (Site site : list) {
                        downMap.put(site.getIndex(),site);
                    }
                }
            } catch (Exception e){
                L.e(TAG, "KEY_DOWN_LIST: ", e);
            }

            String upDown;
            String lastDirectionStr = Cache.getString(KEY_LAST_DIRECTION);
            if(!TextUtils.isEmpty(lastDirectionStr)){
                upDown = lastDirectionStr;
            } else {
                upDown = "01";
            }

            String inOut;
            String lastInOutStr = Cache.getString(KEY_LAST_INOUT);
            if(!TextUtils.isEmpty(lastInOutStr)){
                inOut = lastInOutStr;
            } else {
                inOut = "01";
            }

            int lastIndex = Cache.getInt(KEY_LAST_INDEX);

            String lastSiteType = Cache.getString(KEY_LAST_SITE_TYPE);

            mStartSiteChName = Cache.getString(KEY_UP_START_END_SITE);
            mEndSiteChName = Cache.getString(KEY_DOWN_START_END_SITE);
            mLineName = Cache.getString(KEY_LINE_NAME);
            mBasePrice = Cache.getString(KEY_PRICE);
            mUpRunTime = Cache.getString(KEY_UP_START_END_TIME);
            mDownRunTime = Cache.getString(KEY_DOWN_START_END_TIME);
            mUpSpecialRunTime = Cache.getString(KEY_UP_SPECIAL_START_END_TIME);
            mDownSpecialRunTime = Cache.getString(KEY_DOWN_SPECIAL_START_END_TIME);

            sendList();
            sendBroadSite(upDown,lastSiteType,lastIndex,inOut);
            sendHeadPromotionSlogan();
        });
    }

    private void sendList(){
        Object[] objects = convertToMixSiteList(upMap, downMap);
        UiMessageUtils.getInstance().send(Event.UPDATE_SITE_LIST.getType(), objects);

        try {
            LinkedList<Site> upSiteList = new LinkedList<>(upMap.values());
            if(!upSiteList.isEmpty()){
                Site upFirstSite = upSiteList.getFirst();
                Log.d(TAG, "sendList: 起点站名：" + mStartSiteChName);
                Log.d(TAG, "sendList: 终点站名：" + mEndSiteChName);
                Log.d(TAG, "sendList: 线路名称：" + mLineName);
                Log.d(TAG, "sendList: 基础票价：" + mBasePrice);
                Log.d(TAG, "sendList: 上行首末：" + mUpRunTime);
                Log.d(TAG, "sendList: 下行首末：" + mDownRunTime);
                Log.d(TAG, "sendList: 上行特殊：" + mUpSpecialRunTime);
                Log.d(TAG, "sendList: 下行特殊：" + mDownSpecialRunTime);
                Log.d(TAG, "缓存线路信息：" + mLineName + " ---- " + mStartSiteChName + " --- " + mEndSiteChName);

                String upStartSite,downStartSite,upDown;
                //判断上下行
                if(mStartSiteChName.equals(upFirstSite.getChName())){
                    upStartSite = mStartSiteChName;
                    downStartSite = mEndSiteChName;
                    upDown = "01";
                } else {
                    upStartSite = mEndSiteChName;
                    downStartSite = mStartSiteChName;
                    upDown = "02";
                }
                UiMessageUtils.getInstance().send(Event.UPDATE_INFO.getType(),new String[]{
                        mLineName,
                        upStartSite,mUpRunTime,mUpSpecialRunTime,
                        downStartSite,mDownRunTime,mDownSpecialRunTime,
                        mBasePrice,upDown
                });
            }
        } catch (Exception e){
            L.e(TAG,"UPDATE_INFO",e);
        }
    }

    public static Object[] convertToMixSiteList(
            LinkedHashMap<Integer,Site> upMap,
            LinkedHashMap<Integer,Site> downMap
    ) {
        Log.e(TAG, "convertToMixSiteList: START ------------------------------------------------------------ ");
        AtomicBoolean isLoopLine = new AtomicBoolean(false);
        LinkedList<DalianSiteAdapter.MixSite> mixSiteList = new LinkedList<>();

        try {
            if(!upMap.isEmpty() || !downMap.isEmpty()){
                LinkedList<Site> originUpSiteList = new LinkedList<>(upMap.values());
                LinkedList<Site> originDownSiteList = new LinkedList<>(downMap.values());
                Log.d(TAG, "原始upList: " + originUpSiteList);
                Log.d(TAG, "原始downList: " + originDownSiteList);

                //如果下行为空，或者上行和下行站点名相同，则认为为回线
                isLoopLine.set(originDownSiteList.isEmpty()
                        || TextUtils.equals(originUpSiteList.toString(),originDownSiteList.toString())
                );

                if(isLoopLine.get()){
                    for (Site site : originUpSiteList) {
                        DalianSiteAdapter.MixSite mixSite = new DalianSiteAdapter.MixSite();
                        mixSite.setUp(true);
                        mixSite.setUpIndex(site.getIndex());
                        mixSite.setChName(site.getChName());
                        mixSite.setEnName(site.getEnName());
                        mixSiteList.add(mixSite);
                    }
                } else {
                    LinkedList<Site> upSiteList = new LinkedList<>(originUpSiteList);
                    LinkedList<String> upNameList = new LinkedList<>();
                    for (Site site : upSiteList) {
                        upNameList.add(site.getChName());
                    }

                    LinkedList<Site> downSiteList = new LinkedList<>(originDownSiteList);
                    Collections.reverse(downSiteList);
                    LinkedList<String> downNameList = new LinkedList<>();
                    for (Site site : downSiteList) {
                        downNameList.add(site.getChName());
                    }

                    Log.d(TAG, "upList: " + upSiteList);
                    Log.d(TAG, "downList: " + downSiteList);
                    Log.d(TAG, "upNameList: " + upNameList);
                    Log.d(TAG, "downNameList: " + downNameList);

                    Collection upSubtract = CollectionUtils.subtract(upNameList, downNameList);
                    Log.d(TAG, "上行差集: " + upSubtract);
                    Collection downSubtract = CollectionUtils.subtract(downNameList,upNameList);
                    Log.d(TAG, "下行差集：" + downSubtract);

                    LinkedList<String> resultNameList = new LinkedList<>();
                    if(downNameList.size() >= upNameList.size()){
                        resultNameList.addAll(upNameList);
                        for (Object o : downSubtract) {
                            String downSubtractName = o != null ? o.toString() : "";
                            int addIndex = downNameList.indexOf(downSubtractName);
                            int addOffset = 0;
                            for (Object o1 : upSubtract) {
                                String upSubtractName = o1 != null ? o1.toString() : "";
                                int i1 = upNameList.indexOf(upSubtractName);
                                if(i1 <= addIndex){
                                    addOffset ++;
                                }
                            }
                            addIndex += addOffset;
                            resultNameList.add(addIndex,downSubtractName);
                        }
                    } else {
                        resultNameList.addAll(downNameList);
                        for (Object o : upSubtract) {
                            String upSubtractName = o != null ? o.toString() : "";
                            int addIndex = upNameList.indexOf(upSubtractName);
                            int addOffset = 0;
                            for (Object o1 : downSubtract) {
                                String downSubtractName = o1 != null ? o1.toString() : "";
                                //有差集则取出并判断偏移多少位
                                int i1 = downNameList.indexOf(downSubtractName);
                                if(i1 <= addIndex){
                                    addOffset ++;
                                }
                            }
                            //计算偏移量后得出最终插入位置
                            addIndex += addOffset;
                            //插入元素
                            resultNameList.add(addIndex,upSubtractName);
                        }
                    }
                    Log.i(TAG, "结果: " + resultNameList);

                    for (int i = 0; i < resultNameList.size(); i++) {
                        DalianSiteAdapter.MixSite mixSite = new DalianSiteAdapter.MixSite();

                        String name = resultNameList.get(i);
                        int upIndex = upNameList.indexOf(name);
                        if(upIndex > -1){
                            Site upSite = upSiteList.get(upIndex);
                            mixSite.setUp(true);
                            mixSite.setUpIndex(upSite.getIndex());
                            mixSite.setChName(upSite.getChName());
                            mixSite.setEnName(upSite.getEnName());
                        }

                        int downIndex = downNameList.indexOf(name);
                        if(downIndex > -1){
                            Site downSite = downSiteList.get(downIndex);
                            mixSite.setDown(true);
                            mixSite.setDownIndex(downSite.getIndex());
                            mixSite.setChName(downSite.getChName());
                            mixSite.setEnName(downSite.getEnName());
                        }

                        if(!mixSite.isUp() && !mixSite.isDown()){
                            continue;
                        }

                        mixSiteList.add(mixSite);
                    }
                }
                Log.d(TAG, "mixSiteList: " + mixSiteList);
                Log.d(TAG, "isLoopLine: " + isLoopLine.get());
            }
        } catch (Exception e){
            L.e(TAG, e);
        }

        Log.e(TAG, "convertToMixSiteList: END ------------------------------------------------------------ ");
        return new Object[]{
                mixSiteList,isLoopLine.get()
        };
    }

    private void sendBroadSite(String upDown,String siteType,int index,String inOut){
        try {
            Log.d(TAG, "报站：" + ("01".equals(upDown) ? "上行" : "下行") + " --- " + index + " --- " + ("01".equals(inOut) ? "到站" : "离站"));

            if(TextUtils.equals("01",upDown)){
                if(!upMap.isEmpty()){
                    Site currSite = upMap.get(index);
                    if(currSite != null){
                        UiMessageUtils.getInstance().send(Event.BROAD_SITE.getType(),new Object[]{currSite,index,inOut,upDown});

                        sendHeadBroadSite(siteType,inOut,upDown,index);
                    }
                }
            } else if(TextUtils.equals("02",upDown)){
                if(!downMap.isEmpty()){
                    Site currSite = downMap.get(index);
                    if(currSite != null){
                        UiMessageUtils.getInstance().send(ZHXPortAgreement.Event.BROAD_SITE.getType(),new Object[]{currSite,index,inOut,upDown});

                        sendHeadBroadSite(siteType,inOut,upDown,index);
                    }
                }
            }
        } catch (Exception e){
            L.e(TAG,"sendBroadSite",e);
        }
    }

    /**
     * 播放宣传用语
     */
    private void sendHeadPromotionSlogan(){
        try {
            String promotionSlogan = Cache.getString(KEY_PROMOTION_SLOGAN,DEFAULT_PROMOTION_SLOGAN);
            L.d(TAG, "sendHeadPromotionSlogan: " + promotionSlogan);
            if(!TextUtils.isEmpty(promotionSlogan)){
                UiMessageUtils.getInstance().send(Event.HEAD_UPDATE_PROMOTION_SLOGAN.getType(),promotionSlogan);
            }
        } catch (Exception e){
            L.e(TAG,"sendHeadPromotionSlogan",e);
        }
    }

    /**
     * 播放头屏报站
     * @param siteType
     * @param upDown
     * @param siteIndex
     */
    private void sendHeadBroadSite(String siteType,String inOut,String upDown,int siteIndex){
        try {
            switch (siteType) {
                case "01"://上行首站
                case "03"://下行首战
                case "02"://上行末站
                case "04"://下行末站
                    String slogan = startEndSloganMap.get(siteType);
                    if(slogan != null && !TextUtils.isEmpty(slogan)){
                        UiMessageUtils.getInstance().send(Event.HEAD_BROAD_SITE.getType(),new Object[]{
                                slogan,TextUtils.equals("01",inOut)
                        });
                        break;
                    }
                default:
                    Site currSite;
                    if(TextUtils.equals("01",upDown)){
                        currSite = upMap.get(siteIndex);
                    } else {
                        currSite = downMap.get(siteIndex);
                    }

                    if(currSite != null){
                        String broadSite;
                        if(TextUtils.equals("01",inOut)){
                            broadSite = "乘客们，" + currSite.getChName() + " 到了！";
                        } else {
                            String siteName;

                            Site site;
                            if(TextUtils.equals("01",upDown)){
                                site = upMap.get(siteIndex);
                            } else {
                                site = downMap.get(siteIndex);
                            }

                            if(site != null){
                                siteName = site.getChName();
                            } else {
                                siteName = currSite.getChName();
                            }
                            broadSite = "乘客们，下一站 " + siteName + "。欢迎乘坐本线路公交车";
                        }
                        UiMessageUtils.getInstance().send(Event.HEAD_BROAD_SITE.getType(),new Object[]{
                                broadSite,TextUtils.equals("01",inOut)
                        });
                    }
                    break;
            }
        } catch (Exception e){
            L.e(TAG,"sendHeadBroadSite",e);
        }
    }

    private final StringBuilder stringBuilder = new StringBuilder();
    @Override
    public void analyzing(SerialHelper serialHelper, ComBean comBean) throws Throwable {
        executorService.submit(() -> {
            try {
                byte[] bytes = comBean.bRec;

                if(bytes == null || bytes.length == 0){
                    return;
                }

                String hex = Utils.bytesToHex(bytes);
                if(!hex.startsWith("81") || !hex.endsWith("58")){
                    if(hex.startsWith("81")){
                        stringBuilder.append(hex);
                        return;
                    } else if(hex.endsWith("58")){
                        stringBuilder.append(hex);
                        bytes = Utils.hexToByteArray(stringBuilder.toString());
                        stringBuilder.setLength(0);
                    } else if(stringBuilder.length() != 0){
                        stringBuilder.append(hex);
                        return;
                    } else {
                        return;
                    }
                }

                byte startByte = bytes[0];

                byte targetAddressByte = (byte) 0xff;
                String targetAddressHex = "";
                if(bytes.length >= 2){
                    targetAddressByte = bytes[1];
                    targetAddressHex = Utils.byteToHex(targetAddressByte);
                }

                byte sourceAddressByte = (byte) 0xff;
                String sourceAddressHex = "";
                if(bytes.length >= 3){
                    sourceAddressByte = bytes[2];
                    sourceAddressHex = Utils.byteToHex(sourceAddressByte);
                }

                byte controlCodeByte = (byte) 0xff;
                String controlCodeHex = "";
                String controlCodeBits = "";
                if(bytes.length >= 4){
                    controlCodeByte = bytes[3];
                    controlCodeHex = Utils.byteToHex(controlCodeByte);
                    controlCodeBits = Utils.byteToBit(controlCodeByte);
                    char[] controlCodeCharArray = controlCodeBits.toCharArray();
                }

                byte mainFlagByte = (byte) 0xff;
                String mainFlagHex = "";
                if(bytes.length >= 5){
                    mainFlagByte = bytes[4];
                    mainFlagHex = Utils.byteToHex(mainFlagByte);
                }

                if(bytes.length >= 6){
                    byte packNumberByte = bytes[5];
                }

                int dataLength = 0;
                if(bytes.length >= 8){
                    byte[] dataLengthBytes = Utils.subBytes(bytes, 6, 2);
                    dataLength = Utils.hex16to10(Utils.bytesToHex(dataLengthBytes));
                }

                byte[] dataBytes = null;
                if(bytes.length >= 8 + dataLength){
                    Log.d(TAG, "analyzing: 数据长度：" + dataLength);
                    dataBytes = Utils.subBytes(bytes, 8, dataLength);
                }

                byte selfSourceAddressByte = Utils.hexToByte(Config.getSourceAddress());

                if(dataBytes == null){
                    return;
                }

                Log.d(TAG, "analyzing: 目标地址：" + targetAddressHex);
                Log.d(TAG, "analyzing: 源地址：" + sourceAddressHex);
                Log.d(TAG, "analyzing: 控制码：" + controlCodeHex);
                Log.d(TAG, "analyzing: 主标识：" + mainFlagHex);

                if(sourceAddressByte == (byte)0x11 && targetAddressByte == (byte)0x30){
                    switch (mainFlagByte) {
                        case (byte)0x41:
                            setLineName(dataBytes);
                            reply(serialHelper,controlCodeHex,mainFlagHex,"");
                            break;
                    }
                }
                else if(sourceAddressByte == (byte)0x11 && targetAddressByte == selfSourceAddressByte){
                    switch (mainFlagByte) {
                        //屏幕共用数据项=========================================
                        //自检命令
                        case (byte)0x04:{
                            L.d(TAG, "analyzing: 自检命令");
                            long l = Long.parseLong(BuildConfig.BUILD_TIME_MILLIS);
                            String date = TimeUtils.millis2String(l, "yyyyMMddHH");

                            String screenWidth = String.valueOf(ScreenUtils.getScreenWidth());
                            screenWidth = Utils.zeroFill(screenWidth,4);

                            String screenHeight = String.valueOf(ScreenUtils.getScreenHeight());
                            screenHeight = Utils.zeroFill(screenHeight,4);

                            String secondData = date + screenWidth + screenHeight;
                            reply(serialHelper,controlCodeHex,mainFlagHex,secondData);
                            break;
                        }
                        //SN码数据
                        case (byte)0x05:{
                            L.d(TAG, "analyzing: SN码数据");
                            if(controlCodeByte == (byte)0x04){
                                setSNCode(dataBytes);
                                reply(serialHelper,controlCodeHex,mainFlagHex,"");
                            } else if(controlCodeByte == (byte)0x01){
                                String secondData = Cache.getString(KEY_SN_CODE);
                                reply(serialHelper,controlCodeHex,mainFlagHex,secondData);
                            }
                            break;
                        }
                        //升级相关
                        case (byte)0x01://写升级信息
                        case (byte)0x02://写升级程序包
                        case (byte)0x03://写升级程序包完成
                            //心跳命令
                        case (byte)0x06:
                            //透传协议总包数数据
                        case (byte)0x07:
                            //透传协议透传数据内容
                        case (byte)0x08:
                            //英文站名显示
                        case (byte)0x09:
                            //出厂测试
                        case (byte)0x0A:
                            reply(serialHelper,controlCodeHex,mainFlagHex,"");
                            break;

                        //设置时间
                        case (byte)0x20:
                            L.d(TAG, "analyzing: 设置时间");
                            reply(serialHelper,controlCodeHex,mainFlagHex,"");
                            break;

                        //车内屏数据帧/LCD屏数据帧=========================================
                        //设置上行站数据
                        case (byte)0x21://总包数
                            L.d(TAG, "analyzing: 上行站总包数");
                            setPackCount(dataBytes,mainFlagByte);
                            reply(serialHelper,controlCodeHex,mainFlagHex,"");
                            break;
                        case (byte)0x22://站点数据
                            L.d(TAG, "analyzing: 上行站站点数据");
                            setSiteData(dataBytes,mainFlagByte);
                            reply(serialHelper,controlCodeHex,mainFlagHex,"");
                            break;
                        //设置下行站数据
                        case (byte)0x23://总包数
                            L.d(TAG, "analyzing: 下行站总包数");
                            setPackCount(dataBytes, mainFlagByte);
                            reply(serialHelper,controlCodeHex,mainFlagHex,"");
                            break;
                        case (byte)0x24://站点数据
                            L.d(TAG, "analyzing: 下行站站点数据");
                            setSiteData(dataBytes,mainFlagByte);
                            reply(serialHelper,controlCodeHex,mainFlagHex,"");
                            break;
                        //设置首末站用语数据
                        case (byte)0x25://总包数数据
                            L.d(TAG, "analyzing: 总包数数据");
                            startEndSloganCount(dataBytes);
                            reply(serialHelper,controlCodeHex,mainFlagHex,"");
                            break;
                        case (byte)0x26://首末站用语数据
                            L.d(TAG, "analyzing: 首末站用语数据");
                            setStartEndSlogan(dataBytes);
                            reply(serialHelper,controlCodeHex,mainFlagHex,"");
                            break;
                        // 设置宣传用语数据
                        case (byte)0x27://总包数数据
//                        promotionSloganPackage(dataBytes);
                            reply(serialHelper,controlCodeHex,mainFlagHex,"");
                            break;
                        case (byte)0x28://宣传用语数据
                            L.d(TAG, "analyzing: 宣传用语数据");
//                        setPromotionSlogan(dataBytes);
                            reply(serialHelper,controlCodeHex,mainFlagHex,"");
                            break;
                        case (byte)0x29:// 删除宣传用语数据
                            L.d(TAG, "analyzing: 删除宣传用语数据");
//                        removePromotionSlogan(dataBytes);
                            reply(serialHelper,controlCodeHex,mainFlagHex,"");
                            break;
                        case (byte)0x2A://宣传用语播放
                            L.d(TAG, "analyzing: 宣传用语播放");
//                        playSlogan(dataBytes);
                            reply(serialHelper,controlCodeHex,mainFlagHex,"");
                            break;
                        case (byte)0x2B://临时宣传用语播放
                            L.d(TAG, "analyzing: 临时宣传用语播放");
                            playTempPromotionSlogan(dataBytes);
                            reply(serialHelper,controlCodeHex,mainFlagHex,"");
                            break;
                        case (byte)0x2C://到离站显示
                            L.d(TAG, "analyzing: 到离站显示");
                            broadInOutSite(dataBytes);
                            reply(serialHelper,controlCodeHex,mainFlagHex,"");
                            break;

                        //头尾牌数据帧=========================================
                        //普通站点信息显示
                        case (byte)0x41:
                            Log.d(TAG, "线路信息: " + Utils.bytesToHex(bytes));
                            Log.d(TAG, "线路数据: " + Utils.bytesToHex(dataBytes));
                            setLineName(dataBytes);
                            reply(serialHelper,controlCodeHex,mainFlagHex,"");
                            break;
                        //特殊站点信息显示
                        case (byte)0x40:
                            //车距信息显示
                        case (byte)0x42:
                            reply(serialHelper,controlCodeHex,mainFlagHex,"");
                            break;

                        //腰牌数据帧=========================================
                        //普通站点信息显示
                        case (byte)0x61:
                            Log.d(TAG, "000 解析线路信息: " + Utils.bytesToHex(bytes));
                            setLineName(dataBytes);
                            reply(serialHelper,controlCodeHex,mainFlagHex,"");
                            break;
                        //特殊站点信息显示
                        case (byte)0x60:
                            //腰牌亮度控制
                        case (byte)0x62:
                            //LCD屏数据帧=========================================
                            //设置车载信息终端编号
                        case (byte)0x70:
                            //设置车载机芯片加密号
                        case (byte)0x71:
                            //后视镜电子路牌=========================================
                            //普通站点信息显示
                        case (byte)0x81:
                            reply(serialHelper,controlCodeHex,mainFlagHex,"");
                            break;
                    }
                }
            } catch (Exception e){
                L.e(TAG,"analyzing",e);
                throw e;
            }
        });
    }

    private void reply(SerialHelper serialHelper,String hex){
        try {
            if(serialHelper != null && serialHelper.isOpen()){
                serialHelper.sendHex(hex.replaceAll(" ",""));
            }
        } catch (Exception e){
            L.e(TAG,"reply",e);
        }
    }

    private void reply(SerialHelper serialHelper,
                        String controlCodeHex,
                        String mainFlagHex,
                        String secondData){
        try {
            //副标识数据
            String secondFlag = "";
            if(!TextUtils.isEmpty(secondData)){
                String secondDataLength = Integer.toHexString(secondData.length() / 2);
                secondDataLength = Utils.zeroFill(secondDataLength,4);

                secondFlag = "01" + secondDataLength + secondData;
            }

            String packDataLength = Integer.toHexString(secondFlag.length() / 2);
            packDataLength = Utils.zeroFill(packDataLength,4);

            String packHex = "01" + packDataLength + secondFlag;

            String hex = "81 11" + Config.getSourceAddress() + controlCodeHex + mainFlagHex + packHex;
            hex = hex.replaceAll(" ","");

            String authCode = Utils.accumulating(hex);
            authCode = Utils.zeroFill(authCode,2);

            String reply = hex + authCode + "58";

            reply(serialHelper,reply);
        } catch (Exception e){
            L.e(TAG,"reply",e);
        }
    }

    private void setSNCode(byte[] dataBytes){
        try {
            String snCode = bytesToString(
                    Utils.subBytes(dataBytes, 3, dataBytes.length - 3)
            );
            Cache.setString(KEY_SN_CODE,snCode);
        } catch (Exception e){
            L.e(TAG,"setSNCode",e);
        }
    }

    private int packCount;
    private int currPackNum;
    private void setPackCount(byte[] dataBytes, byte mainFlagByte){
        try {
            Map<Byte, byte[]> resolve = resolve(dataBytes);

            this.packCount = bytesToInt(resolve.get((byte)0x01));
            this.currPackNum = 0;
        } catch (Exception e){
            L.e(TAG,"setPackCount",e);
        }
    }

    private void setSiteData(byte[] dataBytes, byte upDown){
        try {
            this.currPackNum += 1;

            if(this.currPackNum == 1){
                if(upDown == (byte)0x22){
                    upMap.clear();
                    downMap.clear();
                } else if(upDown == (byte)0x24){
                    downMap.clear();
                }
            }

            Map<Byte, byte[]> map = resolve(dataBytes);
            for (Map.Entry<Byte, byte[]> byteEntry : map.entrySet()) {
                byte[] value = byteEntry.getValue();

                int index = 0;
                int siteIndex = byteToInt(value[index]);

                index += 1;
                int chNameLength = byteToInt(value[index]);

                index += 1;
                String chName = bytesToString(Utils.subBytes(value, index, chNameLength));

                index += chNameLength;
                int enNameLength = byteToInt(value[index]);

                index += 1;
                String enName = bytesToString(Utils.subBytes(value, index, enNameLength));

                Site site = new Site(siteIndex, chName, enName);
                if(upDown == (byte)0x22){
                    upMap.put(site.getIndex(),site);
                } else if(upDown == (byte)0x24){
                    downMap.put(site.getIndex(),site);
                }
            }

            if(this.currPackNum == this.packCount){
                if(upDown == (byte)0x22){
                    Log.d(TAG, "setUpSiteData:上行： " + upMap.values());
                    Cache.setString(KEY_UP_LIST, GsonUtils.toJson(upMap.values()));
                    sendList();
                } else if(upDown == (byte)0x24){
                    Cache.setString(KEY_DOWN_LIST, GsonUtils.toJson(downMap.values()));
                    Log.d(TAG, "setUpSiteData:下行： " + downMap.values());
                    sendList();
                }
            }
        } catch (Exception e){
            L.e(TAG,"setUpSiteData",e);
        }

    }

    private void broadInOutSite(byte[] dataBytes){
        try {
            Map<Byte, byte[]> map = resolve(dataBytes);

            //01上行 02下行
            String upDown = bytesToHex(map.get((byte) 0x01));

            //01上行首站 02上行末站 03下行首站 04下行末站 05中间站
            String siteType = bytesToHex(map.get((byte) 0x02));

            //01到站 02离站
            String inOut = bytesToHex(map.get((byte) 0x03));

            //站序
            int siteIndex = bytesToInt(map.get((byte) 0x04));

            Cache.setString(KEY_LAST_DIRECTION,upDown);
            Cache.setString(KEY_LAST_INOUT,inOut);
            Cache.setInt(KEY_LAST_INDEX,siteIndex);
            Cache.setString(KEY_LAST_SITE_TYPE,siteType);

            sendBroadSite(upDown,siteType,siteIndex,inOut);
        } catch (Exception e){
            L.e(TAG,"broadInOutSite",e);
        }
    }

    private String mStartSiteChName;
    private String mEndSiteChName;
    private String mLineName;
    private String mBasePrice;
    private String mUpRunTime;
    private String mDownRunTime;
    private String mUpSpecialRunTime;
    private String mDownSpecialRunTime;
    private void setLineName(byte[] dataBytes){
        try {
            Map<Byte, byte[]> map = resolve(dataBytes);

            mStartSiteChName = bytesToString(map.get((byte)0x01));

            mEndSiteChName = bytesToString(map.get((byte)0x02));

            mLineName = bytesToString(map.get((byte)0x03));

            mBasePrice = bytesToString(map.get((byte)0x08));

            mUpRunTime = bytesToString(map.get((byte)0x09));

            mDownRunTime = bytesToString(map.get((byte)0x0A));

            mUpSpecialRunTime = bytesToString(map.get((byte)0x0B));

            mDownSpecialRunTime = bytesToString(map.get((byte)0x0C));

            L.d(TAG,"解析线路信息：\n"
                    + mLineName + "，"
                    + mStartSiteChName + "，" + mUpRunTime + "，"
                    + mEndSiteChName + "，" + mDownRunTime + "，"
                    + mUpSpecialRunTime + "，"
                    + mDownSpecialRunTime
            );

            Cache.setString(KEY_UP_START_END_SITE,mStartSiteChName);
            Cache.setString(KEY_DOWN_START_END_SITE,mEndSiteChName);
            Cache.setString(KEY_LINE_NAME,mLineName);
            Cache.setString(KEY_PRICE,mBasePrice);
            Cache.setString(KEY_UP_START_END_TIME,mUpRunTime);
            Cache.setString(KEY_DOWN_START_END_TIME,mDownRunTime);
            Cache.setString(KEY_UP_SPECIAL_START_END_TIME,mUpSpecialRunTime);
            Cache.setString(KEY_DOWN_SPECIAL_START_END_TIME,mDownSpecialRunTime);
        } catch (Exception e){
            L.e(TAG,"setLineName",e);
        }
    }

    private int startEndSloganPackCount;
    private int startEndSloganCurrPackNum;
    private void startEndSloganCount(byte[] dataBytes){
        try {
            L.d(TAG, "startEndSloganCount: " + Utils.bytesToHex(dataBytes));
            Map<Byte, byte[]> resolve = resolve(dataBytes);
            for (Map.Entry<Byte, byte[]> byteEntry : resolve.entrySet()) {
                L.d(TAG, "startEndSloganCount: " + Utils.byteToHex(byteEntry.getKey()) + " --- " + Utils.bytesToHex(byteEntry.getValue()));

                String s = Utils.bytesToHex(byteEntry.getValue());
                this.startEndSloganPackCount = Utils.hex16to10(s);
                this.startEndSloganCurrPackNum = 0;
                L.d(TAG, "分包数: --- " + startEndSloganPackCount);
            }
        } catch (Exception e){
            L.e(TAG,"startEndSloganCount",e);
        }
    }

    private final Map<String,String> startEndSloganMap = new HashMap<>();
    private void setStartEndSlogan(byte[] dataBytes){
        try {
            this.startEndSloganCurrPackNum += 1;

            if(this.startEndSloganCurrPackNum == 1){
                startEndSloganMap.clear();
            }

            L.d(TAG, "setStartEndSlogan: " + Utils.bytesToHex(dataBytes));
            Map<Byte, byte[]> resolve = resolve(dataBytes);
            for (Map.Entry<Byte, byte[]> byteEntry : resolve.entrySet()) {
                //01 上行起点
                //02 上行终点
                //03 下行起点
                //04 下行终点
                String type = Utils.byteToHex(byteEntry.getKey());
                L.d(TAG, "setStartEndSlogan: " + type + " --- " + Utils.bytesToHex(byteEntry.getValue()));

                byte[] bytes = Utils.subBytes(byteEntry.getValue(), 1, byteEntry.getValue().length - 1);
                String content = Utils.hexStringToString(Utils.bytesToHex(bytes));

                L.d(TAG, "setStartEndSlogan: " + content);

                startEndSloganMap.put(type,content);
            }
        } catch (Exception e){
            L.e(TAG,"setStartEndSlogan",e);
        }
    }

    private void playTempPromotionSlogan(byte[] dataBytes){
        try {
            Map<Byte, byte[]> resolve = resolve(dataBytes);

            String slogan = bytesToString(resolve.get((byte) 0x01));

            if(TextUtils.isEmpty(slogan)){
                Cache.remove(KEY_PROMOTION_SLOGAN);
            } else {
                Cache.setString(KEY_PROMOTION_SLOGAN,slogan);
            }

            sendHeadPromotionSlogan();
        } catch (Exception e){
            L.e(TAG,"playTempPromotionSlogan",e);
        }
    }

    private Map<Byte,byte[]> resolve(byte[] dataBytes){
        LinkedHashMap<Byte,byte[]> map = new LinkedHashMap<>();
        try {
            int type = 0;
            int index = 0;
            byte flag = 0x00;
            int length = 0;
            while (index < dataBytes.length){
                if(type == 0){
                    flag = dataBytes[index];

                    index += 1;
                    type = 1;
                } else if(type == 1){
                    byte[] lengthBytes = Utils.subBytes(dataBytes, index, 2);
                    length = Utils.hex16to10(Utils.bytesToHex(lengthBytes));

                    index += 2;
                    type = 2;
                } else {
                    byte[] bytes = Utils.subBytes(dataBytes,index,length);
                    map.put(flag,bytes);

                    index += length;
                    type = 0;
                }
            }
        } catch (Exception e){
            L.e(TAG,"resolve",e);
        }
        return map;
    }

    private String bytesToHex(byte[] bytes){
        if(bytes != null){
            return Utils.bytesToHex(bytes);
        }
        return "";
    }

    private int byteToInt(byte b){
        return Utils.hex16to10(Utils.byteToHex(b));
    }

    private int bytesToInt(byte[] bytes){
        if(bytes != null){
            return Utils.hex16to10(Utils.bytesToHex(bytes));
        }
        return -1;
    }

    private String bytesToString(byte[] bytes){
        if(bytes != null){
            return Utils.hexStringToString(Utils.bytesToHex(bytes));
        }
        return "";
    }

    private String getSiteListLog(Site[] sites){
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < sites.length; i++) {
            stringBuilder.append(sites[i].getChName());
            if(i < sites.length - 1){
                stringBuilder.append(" → ");
            }
        }
        return stringBuilder.toString();
    }

    public static class Site {
        private int index;
        private String chName;
        private String enName;

        public Site(int index, String chName, String enName) {
            this.index = index;
            this.chName = chName;
            this.enName = enName;
        }

        public int getIndex() {
            return index;
        }

        public String getChName() {
            return chName;
        }

        public String getEnName() {
            return enName;
        }

        public void setEnName(String enName) {
            this.enName = enName;
        }

        @Override
        public String toString() {
            return index + "：" + chName;
        }
    }

    public enum Event{
        UPDATE_LINE_NAME(511),
        UPDATE_SITE_LIST(512),
        BROAD_SITE(513),
        UPDATE_START_END(514),

        UPDATE_INFO(515),

        HEAD_UPDATE_PROMOTION_SLOGAN(516),

        HEAD_BROAD_SITE(517),
        ;

        private final int type;

        Event(int type) {
            this.type = type;
        }

        public int getType(){
            return type;
        }
    }

    public void test(){
        new Thread(() -> {
            File file = new File(Environment.getExternalStorageDirectory(),"test.txt");
            List<String> strings = FileIOUtils.readFile2List(file);
            for (String string : strings) {
                if(string.contains("<---")){
                    String[] split = string.split("<---");
                    String s = split[1].replaceAll(" ","");
                    if(!TextUtils.isEmpty(s)){
                        if(s.contains("当前协议:智达")){
                            Log.d(TAG, "test: ---------------------------------------------------------------------------");
                        }

                        try {
                            Log.d(TAG, "test: " + s);
                            byte[] bytes = Utils.hexToByteArray(s);
                            ComBean comBean = new ComBean("",bytes,bytes.length);
                            analyzing(null,comBean);
                        } catch (Throwable e) {
//                            L.e(TAG, "test: ", e);
                        }

                        if(s.contains("当前协议:智达")){
                            try {
                                Thread.sleep(20000);
                            } catch (InterruptedException e) {

                            }
                        }

                    }
                }
            }
        }).start();
    }

    public void test2(){
        new Thread(() -> {
            File file = new File(Environment.getExternalStorageDirectory(),"test2.txt");
            List<String> strings = FileIOUtils.readFile2List(file);
            for (String string : strings) {
                if(string.contains("←◆")){
                    String[] split = string.split("←◆");
                    String s = split[1].replaceAll(" ","");
                    s = s.replaceAll(" ","");
                    if(!TextUtils.isEmpty(s)){
                        if(s.contains("当前协议:智达")){
                            Log.d(TAG, "test: ---------------------------------------------------------------------------");
                        }

                        try {
                            Log.d(TAG, "test: " + s);
                            byte[] bytes = Utils.hexToByteArray(s);
                            ComBean comBean = new ComBean("",bytes,bytes.length);
                            analyzing(null,comBean);
                        } catch (Throwable e) {
//                            L.e(TAG, "test: ", e);
                        }

                        if(s.contains("当前协议:智达")){
                            try {
                                Thread.sleep(20000);
                            } catch (InterruptedException e) {

                            }
                        }

                    }
                }
            }
        }).start();
    }

    private int testIndex = 0;
    private String testInOut = "02";
    private String testUpDown = "02";
    public void test1(){
        if(testInOut.equals("01")){
            testInOut = "02";
            testIndex += 1;
        } else if(testInOut.equals("02")){
            testInOut = "01";
        }

        UiMessageUtils.getInstance().send(ZHXPortAgreement.Event.BROAD_SITE.getType(),new Object[]{null,testIndex,testInOut,testUpDown});
//        L.d(TAG, "test1: " + Event.HEAD_BROAD_SITE.getType());
//        UiMessageUtils.getInstance().send(Event.HEAD_BROAD_SITE.getType(),"临时播报测试");
    }
}
